﻿using System.ComponentModel;
using SvgViewer.Entities;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Threading;
using Microsoft.VisualBasic.FileIO;
using SvgViewer.Core;
using SvgViewer.Utilities;
using SearchOption = System.IO.SearchOption;

namespace SvgViewer.ViewModel;

/// <summary>
/// 主视图ViewModel
/// </summary>
public class MainViewModel : ViewModelBase
{
#region Fields...

    private SvgInfo                       selectedSvg;
    private FolderInfo                    selectedFolder;
    private string                        search;
    private bool                          loading;
    private DispatcherTimer               timer;
    private ObservableCollection<SvgInfo> imageSource;
    private CancellationTokenSource       cancelSource;
    private int                           imageCount;
    private bool                          haveSearch;
    private bool                          showPenetrate;

#endregion
    /// <summary>
    /// 构造
    /// </summary>
    public MainViewModel()
    {
        SelectedFolderChangedCommand = new RelayCommand(OnSelectedFolderChanged);
        PenetrateFolderCommand       = new RelayCommand(_ => LoadImages(true));
        CancelCommand = new RelayCommand(_ => {
            cancelSource.Cancel();
        });
        ExportCommand = new RelayCommand(args => Export(args as SvgInfo), args => HasSelected(args as SvgInfo));
        AboutCommand  = new RelayCommand(_ => OpenAbout());
        OpenDetailCommand =
            new RelayCommand(args => OpenDetail(args as SvgInfo), args => HasSelected(args as SvgInfo));
        OpenInFileExplorerCommand = new RelayCommand(args => OpenInFileExplorer(args as SvgInfo),
            args => HasSelected(args as SvgInfo));
        DeleteSvgCommand = new RelayCommand(args => Delete(args as SvgInfo), args => HasSelected(args as SvgInfo));

        imageSource = new ObservableCollection<SvgInfo>();
        Images = new CollectionViewSource {
            IsLiveSortingRequested = true, Source = imageSource
        };

        Images.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
        Images.Filter += ImagesFilter;
    }

#region Command...

    /// <summary>
    /// 穿透文件夹命令
    /// </summary>
    public RelayCommand PenetrateFolderCommand { get; }

    /// <summary>
    /// 选中的目录改变命令
    /// </summary>
    public RelayCommand SelectedFolderChangedCommand { get; }

    /// <summary>
    /// 取消加载命令
    /// </summary>
    public RelayCommand CancelCommand { get; }

    /// <summary>
    /// 打开明细视图命令
    /// </summary>
    public RelayCommand OpenDetailCommand { get; }

    /// <summary>
    /// 在资源管理器中打开命令
    /// </summary>
    public RelayCommand OpenInFileExplorerCommand { get; }

    /// <summary>
    /// 打开导出视图命令
    /// </summary>
    public RelayCommand ExportCommand { get; }

    /// <summary>
    /// 打开关于视图命令
    /// </summary>
    public RelayCommand AboutCommand { get; }

    /// <summary>
    /// 删除SVG命令
    /// </summary>
    public RelayCommand DeleteSvgCommand { get; }

#endregion

#region Properties...

    /// <summary>
    /// 获取Svg图像列表
    /// </summary>
    public CollectionViewSource Images { get; }

    /// <summary>
    /// 获取文件夹列表
    /// </summary>
    public ObservableCollection<FolderInfo> Folders { get; private set; }

    /// <summary>
    /// 获取或设置选中的Svg图像
    /// </summary>
    public SvgInfo SelectedSvg {
        get => selectedSvg;
        set => this.SetValue(ref selectedSvg, value, nameof(SelectedSvg));
    }

    /// <summary>
    /// 获取或设置搜索内容
    /// </summary>
    public string Search {
        get => search;
        set =>
            SetValue(ref search, value, nameof(Search), () => {
                timer.Stop();
                timer.Start();
            });
    }

    /// <summary>
    /// 获取 图像总数描述信息
    /// </summary>
    public string CountDesc => string.Format(LanguageManager.Instance.GetText("MainView.SvgCount"), imageCount);

    /// <summary>
    /// 获取当前目录中是否有图像文件
    /// </summary>
    public bool HaveImage => imageCount > 0;

    /// <summary>
    /// 获取当前是否加载中状态
    /// </summary>
    public bool Loading {
        get => loading;
        private set => SetValue(ref loading, value, nameof(Loading));
    }

    /// <summary>
    /// 获取或设置图像大小
    /// </summary>
    public double ImageSize {
        get => Properties.Settings.Default.ImageSize;
        set {
            if (value != Properties.Settings.Default.ImageSize)
            {
                Properties.Settings.Default.ImageSize = value;
                Properties.Settings.Default.Save();
                this.OnPropertyChanged(nameof(ImageSize));
            }
        }
    }

    /// <summary>
    /// 获取或设置当前选中的目录
    /// </summary>
    public FolderInfo SelectedFolder {
        get => selectedFolder;
        set =>
            this.SetValue(ref selectedFolder, value, nameof(SelectedFolder), () => {
                search = string.Empty;
                this.OnPropertyChanged(nameof(Search));
                this.ShowPenetrate = false;
                LoadImages(false);
                Properties.Settings.Default.SelectedPath = value.Path;
                Properties.Settings.Default.Save();
            });
    }

    /// <summary>
    /// 获取是否显示穿透文件夹按钮
    /// </summary>
    public bool ShowPenetrate {
        get => showPenetrate;
        private set => SetValue(ref showPenetrate, value, nameof(ShowPenetrate));
    }

#endregion

#region Events...

    private void OpenAbout()
    {
        ViewService?.Show("about");
    }

    private void OpenDetail(SvgInfo info)
    {
        if (info == null)
        {
            return;
        }

        ViewService?.Show("detail", info);
    }

    private void OpenInFileExplorer(SvgInfo info)
    {
        if (info == null)
        {
            return;
        }

        try
        {
            Process.Start("explorer", $"/select,\"{info.FilePath}\"");
        }
        catch
        {
            // ignored
        }
    }

    private void ImagesFilter(object sender, FilterEventArgs e)
    {
        if (haveSearch == false)
        {
            imageCount++;
            e.Accepted = true;
        }
        else
        {
            if (e.Item is SvgInfo info)
            {
                if (info.Name.IndexOf(search, StringComparison.OrdinalIgnoreCase) != -1)
                {
                    imageCount++;
                    e.Accepted = true;
                }
                else
                {
                    e.Accepted = false;
                }
            }
            else
            {
                e.Accepted = false;
            }
        }
    }

    private void OnSelectedFolderChanged(object args)
    {
        var ev = args as RoutedPropertyChangedEventArgs<object>;
        this.SelectedFolder = ev?.NewValue as FolderInfo;
    }

    private void Export(SvgInfo info)
    {
        if (info == null)
        {
            return;
        }

        ViewService?.Show("export", info);
    }

    private void TimerTick(object sender, EventArgs e)
    {
        timer.Stop();
        imageCount = 0;
        haveSearch = !string.IsNullOrEmpty(search);
        Images.View.Refresh();

        this.OnPropertyChanged(nameof(CountDesc));
        this.OnPropertyChanged(nameof(HaveImage));
    }

    private void Delete(SvgInfo info)
    {
        if (info == null)
        {
            return;
        }

        FileSystem.DeleteFile(info.FilePath, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);
        imageSource.Remove(info);
    }

#endregion

#region Methods...

    private bool HasSelected(SvgInfo args)
    {
        return args != null;
    }

    /// <summary>
    /// 
    /// </summary>
    protected override void OnInitializeInRuntime()
    {
        base.OnInitializeInRuntime();
        Folders = FolderHandle.Instance.GetFolders();
        timer = new DispatcherTimer {
            Interval = TimeSpan.FromMilliseconds(300), IsEnabled = false
        };
        timer.Tick += TimerTick;
        ImageSize  =  Properties.Settings.Default.ImageSize;
    }

    private void LoadImages(bool penetration)
    {
        imageCount = 0;
        imageSource.Clear();
        cancelSource  = new CancellationTokenSource();
        ShowPenetrate = false;
        if (SelectedFolder?.FolderType != FolderType.Folder)
        {
            this.OnPropertyChanged(nameof(CountDesc));
            this.OnPropertyChanged(nameof(HaveImage));
            return;
        }

        var option = penetration ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
        Loading = true;
        Task.Factory.StartNew(() => {
            var dir = new DirectoryInfo(SelectedFolder.Path);
            try
            {
                foreach (var file in dir.GetFiles("*.svg", option))
                {
                    if (file.Length < 100)
                    {
                        continue;
                    }

                    if (cancelSource.IsCancellationRequested)
                    {
                        Loading = false;
                        ViewService.Dispatcher.Invoke(() => {
                            this.OnPropertyChanged(nameof(CountDesc));
                            this.OnPropertyChanged(nameof(HaveImage));
                        });

                        break;
                    }

                    ViewService.Dispatcher.InvokeAsync(() => {
                            imageSource.Add(new SvgInfo(file.FullName));
                        },
                        DispatcherPriority.Background, cancelSource.Token);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

            ViewService.Dispatcher.BeginInvoke(() => {
                this.OnPropertyChanged(nameof(CountDesc));
                this.OnPropertyChanged(nameof(HaveImage));
                Loading = false;
                if (imageSource.Count > 0)
                {
                    if (cancelSource.IsCancellationRequested)
                    {
                        ViewService.ShowTip(string.Format(LanguageManager.Instance.GetText("MainView.FileLoadCanceled"),
                            imageCount));
                    }
                    else
                    {
                        ViewService.ShowTip(
                            string.Format(LanguageManager.Instance.GetText("MainView.FileLoadCompleted"), imageCount));
                    }
                }

                ShowPenetrate = imageSource.Count == 0 && selectedFolder != null;
                cancelSource.Dispose();
            }, DispatcherPriority.Background);
        });
    }

#endregion
}